-- ArtifactM
-- Created by chengb Apr/1/2015
-- 神器模块

module("ArtifactM", package.seeall);


-- 神器配置表
local artifactTable = {};
local riskerIds = {};  -- 冒险系神器
local wizardIds = {};   -- 魔法系神器
local fighterIds = {};    -- 战斗系神器

-- 所有的神器信息
local items = {};

local RISK_RELIC_ID = 6000;
local FIGHT_RELIC_ID = 6010;
local WIZARD_RELIC_ID = 6020;

-- 神器起始编号
local ARTIFACT_START_ID = 6001;

local whenDoItemBonus;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 载入神器配置表
    artifactTable = {};
    items = {};
    riskerIds = {};
    wizardIds = {};
    fighterIds = {};
    local artifact_CSV = get_artifact_CSV();
    for i = 1, #artifact_CSV do
        local classId = artifact_CSV[i].class_id;

        -- 解析耐久价格
        artifactTable[classId] = parseDurabilityPrice(artifact_CSV[i])

        items[artifact_CSV[i].class_id] = artifact_CSV[i];

        local department = artifact_CSV[i].department;

        if department == EQUIP_STYLE_SPECIAL then
            table.insert(riskerIds, classId);
        elseif department == EQUIP_STYLE_PHYSIC then
            table.insert(fighterIds, classId);
        else
            table.insert(wizardIds, classId);
        end
    end

    artifact_CSV = {};
end

-- 初始化
function init()
    loadCsv();

    EventMgr.removeAll("ArtifactM");
    EventMgr.register("ArtifactM", event.DO_BONUS, whenDoItemBonus);
end

-- 获取配置表中的所有神器
function getAllItems()
    return items;
end

-- 解析耐久价格
function parseDurabilityPrice(row)
    local arr = {};
    local fields = { "durability_price1", "durability_price2"};

    for _, field in pairs(fields) do
        local str = row[field];
        if string.startWith(str, "1(") and string.endWith(str, ")") then
            -- 物品消耗
            local temp = string.explode(string.sub(str, 3, -2), ",");
            row[field] = {1, tonumber(temp[1]), tonumber(temp[2])};
        elseif string.startWith(str, "2(") and string.endWith(str, ")") then
            -- 属性消耗
            local temp = string.explode(string.sub(str, 3, -2), ",");
            row[field] = {2, temp[1], tonumber(temp[2])};
        else
            assert(false, "耐久价格配置有误。");
        end
    end

    return row;
end

-- 检索配置
function query(classId, path)
    local m = artifactTable[classId];
    if nil == m then
        return nil;
    end
    if (path == "" or path == nil) then
        return m;
    else
        return m[path];
    end
end

-- 是否是神器
function isArtifact(classId)
    local itemInfo = ItemM.query(classId);
    if not itemInfo then
        return false;
    end

    return itemInfo["type"] == ITEM_TYPE_ARTIFACT;
end

-- 是否是神器碎片
function isArtifactScrap(classId)
    local itemInfo = ItemM.query(classId);
    if not itemInfo then
        return false;
    end

    return itemInfo["type"] == ITEM_TYPE_ARTIFACT_SCRAP;
end

-- 获取神器耐久度
function getDurability(classId)
    if not isArtifact(classId) then
        return 0;
    end

    local duraInfo = ME.user.dbase:query("artifact_durability", {});
    return duraInfo[classId] or 0;
end

-- 更新神器等级数据
function updateArtifactLevel(buff)
    local buffer = Buffer.create(buff);
    local offset = 1;
    local size = Buffer.size(buffer);
    local levelInfo = {};
    while (offset <= size) do
        local level = Buffer.getu8(buffer, offset);
        if (level > 0) then
            -- 服务器的下标从0开始，而lua的下标从1开始，因此这里需要-1
            local classId = ARTIFACT_START_ID + offset - 1;
            levelInfo[classId] = level;
        end

        -- 继续下一个道具
        offset = offset + 1;
    end

    ME.user.dbase:set("artifact_level", levelInfo);
end

-------------------------------
-- 计算恢复耐久的消耗
-- @param classId         神器编号
-- @param needDurability  需要恢复的耐久值（如果没有传入，默认恢复全部）
function calcRecoverDuraCost(classId, needDurability)
    if not isArtifact(classId) then
        -- 不是神器
        return {};
    end

    local curDurability = getDurability(classId);
    local maxDurability = artifactTable[classId]["max_durability"];

    -- 如果没有传入耐久度，默认恢复全部
    if needDurability == nil then
        needDurability = maxDurability - curDurability;
    end

    if needDurability < 0 or curDurability + needDurability > maxDurability then
        trace("ArtifactM", "需要的耐久值(%d)非法", needDurability);
        return {};
    end

    -- 优先使用价格1，价格1无法支付时，使用价格2
    local pieceCost = artifactTable[classId]["durability_price1"];
    local moneyCost = table.deepcopy(artifactTable[classId]["durability_price2"]);

    -- 神器修复价格降低
    local prop = PropM.combine(ME.user, "repair_cost", 1);
    moneyCost[3] = PropM.apply(prop, moneyCost[3]);

    local priceList = {pieceCost, moneyCost};
    for _, price in pairs(priceList) do
        local totalCost = price[3] * needDurability;

        if price[1] == 1 then
            if ItemM.getAmount(ME.user, price[2]) >= totalCost then
                price = table.append(price, {});
                price[3] = price[3] * needDurability;
                return price;
            end
        elseif price[1] == 2 then
            if ME.user:queryAttrib(price[2]) >= totalCost then
                price = table.append(price, {});
                price[3] = price[3] * needDurability;
                return price;
            end
        else
            error("价格配置错误。");
        end
    end

    -- 两种价格都无法支付，默认返回价格2
    return { moneyCost[1], moneyCost[2], moneyCost[3] * needDurability};
end

--------------------
-- 计算修复所花费的金币数
-- @param classId         神器编号
-- @param needDurability  需要恢复的耐久值（如果没有传入，默认恢复全部）
function calcRecoverCostMoney(classId, needDurability)
    if not isArtifact(classId) then
        -- 不是神器
        return {};
    end

    local curDurability = getDurability(classId);
    local maxDurability = artifactTable[classId]["max_durability"];

    -- 如果没有传入耐久度，默认恢复全部
    if needDurability == nil then
        needDurability = maxDurability - curDurability;
    end

    if needDurability < 0 or curDurability + needDurability > maxDurability then
        trace("ArtifactM", "需要的耐久值(%d)非法", needDurability);
        return {};
    end

    -- 默认返回价格2
    local price2 = artifactTable[classId]["durability_price2"];
    return { price2[1], price2[2], price2[3] * needDurability};
end

--------------------
-- 获取能修复的耐久点数和消耗
-- @param classId         神器编号
-- 返回当前碎片能修复的点数和消耗
function getCanRecoverNum(classId)
    if not isArtifact(classId) then
        -- 不是神器
        return {};
    end

    local curDurability = getDurability(classId);
    local maxDurability = artifactTable[classId]["max_durability"];

    -- 如果没有传入耐久度，默认恢复全部

    local maxCanReDurability = maxDurability - curDurability;


    if maxCanReDurability < 0 or curDurability + maxCanReDurability > maxDurability then
        trace("ArtifactM", "需要的耐久值(%d)非法", maxCanReDurability);
        return {};
    end

    -- 神器碎片修复
    local price1 = artifactTable[classId]["durability_price1"];

    -- 拥有神器数
    local hasCount = ItemM.getAmount(ME.user, price1[2]);
    local recoverNum = math.min(math.floor(hasCount/price1[3]), maxCanReDurability);
    local needChip = recoverNum*price1[3];
    return {recoverNum, needChip};
end


--------------------
-- 判断能否恢复耐久
-- @param classId        神器编号
-- @param needDurability 需要恢复的耐久值
function canRecoverDurability(classId, needDurability)
    if not isArtifact(classId) then
        -- 不是神器
        return false;
    end

    if ItemM.getAmount(ME.user, classId) <= 0 then
        -- 玩家没有该神器
        return false;
    end

    local curDurability = getDurability(classId);
    local maxDurability = artifactTable[classId]["max_durability"];
    if curDurability >= maxDurability then
        -- 耐久度已满
        return false;
    end

    -- 计算消耗
    local arr = calcRecoverDuraCost(classId, needDurability);
    if #arr == 0 then
        trace("ArtifactM", string.format("获取不到神器(%d)恢复耐久的消耗。", classId));
        return false;
    end

    if arr[1] == 1 then
        -- 物品消耗
        if ItemM.getAmount(ME.user, arr[2]) < arr[3] then
            -- 物品不足
            return arr;
        end

    elseif arr[1] == 2 then
        -- 属性消耗
        if ME.user:queryAttrib(arr[2]) < arr[3] then
            -- 属性不足
            return arr;
        end
    else
        error("消耗格式错误。");
    end

    return true;
end

-----------------------------
-- 恢复耐久
-- @param classId        神器编号
-- @param needDurability 需要恢复的耐久值
-- @param priceIndex
function recoverDurability(classId, needDurability, priceIndex)

    local curDurability = getDurability(classId);
    local maxDurability = artifactTable[classId]["max_durability"];

    -- 如果没有传入耐久度，默认恢复全部
    if needDurability == nil then
        needDurability = maxDurability - curDurability;
    end

    if true ~= canRecoverDurability(classId, needDurability) then
        -- 不满足恢复条件
        return false;
    end

    -- 直接请求服务器恢复
    Operation.cmd_recover_artifact_dura(classId, needDurability, priceIndex);

    return true;
end

--------------------------
-- 设置耐久
-- @param classId   神器编号
-- @param amount    耐久值
function setDurability(classId, amount)
    if not isArtifact(classId) then
        -- 不是神器
        return 0;
    end

    if ItemM.getAmount(ME.user, classId) <= 0 then
        -- 包裹中没有该神器
        return 0;
    end

    -- 不能超过耐久上限
    local maxDurability = artifactTable[classId]["max_durability"];
    amount = math.min(maxDurability, amount);

    -- 修改耐久
    trace("ArtifactM", "神器(%d)的耐久更新为：%d", classId, amount);
    local duraInfo = ME.user.dbase:query("artifact_durability", {});
    duraInfo[classId] = amount;

    return amount;
end

---------------------------
-- 消耗耐久
-- @param classId   神器编号
-- @param amount    需要扣除的耐久值
function costDurability(classId, amount)
    if not isArtifact(classId) then
        -- 不是神器
        return false;
    end

    if ItemM.getAmount(ME.user, classId) <= 0 then
        -- 包裹中没有该神器
        return false;
    end

    local curDurability = getDurability(classId);
    if curDurability < amount then
        -- 耐久度不足
        return false;
    end

    -- 更新耐久
    setDurability(classId, curDurability - amount);

    return true;
end

----------------------------
-- 能否携带神器
-- @param classId  神器编号
function canCarryArtifact(classId)
    if not isArtifact(classId) then
        -- 不是神器，不能携带
        return false;
    end

    local curArtifact = getCarryArtifact();
    if curArtifact == classId then
        -- 已经携带，不可重复携带
        return false;
    end

    local needDurability = FormulaM.invoke("CALC_CARRY_ARTIFACT_DURA_COST");
    local curDurability  = getDurability(classId);
    if curDurability < needDurability then
        -- 耐久度不足
        return false;
    end

    return true;
end

------------------------------
-- 携带神器
-- @param classId  神器编号
function carryArtifact(classId)
    if true ~= canCarryArtifact(classId) then
        -- 不满足携带条件
        return false;
    end

    -- 计算携带神器需要的消耗的耐久
    local needDurability = FormulaM.invoke("CALC_CARRY_ARTIFACT_DURA_COST");

    if not costDurability(classId, needDurability) then
        trace("ArtifactM", "无法携带神器，耐久扣除失败。");
        return false;
    end

    ME.user.dbase:set("carry_artifact", classId);

    return true;
end

-- 获取当前携带的神器
function getCarryArtifact()
    return ME.user.dbase:query("carry_artifact");
end

------------------------------
-- 根据神器获取的神器碎片信息
-- @param classId  神器编号
-- @return 返回合成神器锁需要的神器碎片编号和数量
function getArtifactScrap(classId)
    local compoundRule = CompoundM.getCompoundRule(classId);
    if not compoundRule then
        -- 没有合成规则
        return nil;
    end

    local product = CompoundM.query(compoundRule, "material");
    if not product then
        -- 没有产物
        return nil;
    end

    local ret = {};
    for key, var in pairs(product) do
        ret = table.append(ret, { key, var});
    end
    return ret;
end

------------------------------
-- 根据神器碎片获取的神器信息
-- @param classId  神器碎片编号
-- @return 返回该碎片对应的神器
function getArtifactByScrap(classId)
    for id, info in pairs(artifactTable) do
        if info.durability_price1[2] == classId then
            return id;
        end
    end
end

----------------------------------
-- 获取合成神器需要的碎片数量
-- @param classId  神器碎片编号
-- @return 返回合成神器所需要的神器碎片数量
function getCompoundScrapAmount(classId)
    local rules = CompoundM.getRulesByType("artifact")
    for i = 1, #rules do
        local material = CompoundM.query(rules[i], "material");
        for key, var in pairs(material) do
            if key == classId then
                return var;
            end
        end
    end
    return 0;
end

----------------------------------
-- 获取神器等级
-- @param classId  神器编号
function getArtifactLevel(classId)
    local levelInfo = ME.user.dbase:query("artifact_level");
    if levelInfo == nil or levelInfo[classId] == nil then
        if ItemM.getAmount(ME.user, classId) > 0
            or EquipM.isEquiped(classId) then
           return 0;
        else
           return -1;
        end
    end

    return levelInfo[classId];
end

----------------------------------
-- 玩家身上是否有神器可以升级
function canArtifactUpgradeOrCompound(extraPara)
    -- 获得配置表中的所有神器
    local items = getAllItems();

    for id, _ in pairs(items) do
        if canCompound(id) == true or canUpgrade(id, extraPara) == true then
            return true;
        end
    end

    return false;
end

-- 玩家身上是否有神器可以合成
function canArtifactCompound()
    -- 获得配置表中的所有神器
    local items = getAllItems();

    for id, _ in pairs(items) do
        if canCompound(id) == true then
            return true;
        end
    end

    return false;
end

----------------------------------
-- 是否能合成神器
-- @param classId  神器编号
function canCompound(classId)
    -- 神器已存在
    if ItemM.getAmount(ME.user, classId) > 0 then
        return "artifact_exist";
    end

    -- 神器等级
    local level = getArtifactLevel(classId);
    if level >= 0 then
        return "alread_compound";
    end

    -- 获取该等级合成的消耗
    local cost = getCompoundCost(classId);
    for _, arr in pairs(cost) do
        if arr[1] == 1 then
            if ItemM.getAmount(ME.user, arr[2]) < arr[3] then
                return "not_enough_scrap_to_compound";
            end
        elseif arr[1] == 2 then
            if ME.user:queryAttrib(arr[2]) < arr[3] then
                return "not_enough_money_to_compound";
            end
        end
    end

    return true;
end

----------------------------------
-- 获取合成消耗
-- @param classId  神器编号
-- @return 合成消耗
function getCompoundCost(classId)
    local level = getArtifactLevel(classId);
    local cost = FormulaM.invoke("CALC_ARTIFACT_UPGRADE_LEVEL_COST", level + 1, classId);

    return cost;
end

----------------------------------
-- 是否能升级神器
-- @param classId  神器编号
function canUpgrade(classId, extraPara)
    -- 神器不存在
    local carryId = ME.user.dbase:query("carry_artifact") or -1;
    if ItemM.getAmount(ME.user, classId) == 0 and carryId ~= classId then
        return "artifact_not_exist";
    end

    -- 神器等级
    local level = getArtifactLevel(classId);

    local maxLevel = FormulaM.invoke("GET_ARTIFACT_MAX_LEVEL");

    -- 达到最大等级
    if level >= maxLevel then
        return "hint_reach_max_level";
    end

    -- 获取该等级升级的消耗
    local cost = getUpgradeCost(classId, extraPara);
    for _, arr in pairs(cost) do
        if arr[1] == 1 then
            if ItemM.getAmount(ME.user, arr[2]) < arr[3] then
                return "not_enough_scrap_to_upgrade";
            end
        elseif arr[1] == 2 then
            if ME.user:queryAttrib(arr[2]) < arr[3] then
                return "not_enough_money_to_upgrade";
            end
        end
    end

    return true;
end

----------------------------------
-- 获取强化消耗
-- @param classId  神器编号
-- @return 强化消耗
function getUpgradeCost(classId, extraPara)
    local level = getArtifactLevel(classId);
    local cost = FormulaM.invoke("CALC_ARTIFACT_UPGRADE_LEVEL_COST", level + 1, classId, extraPara);

    return cost;
end

----------------------------------
-- 获取强化属性
-- @param classId   神器编号
-- @return          强化属性的文本
function getUpgradePropDesc(classId, level)
    local desc = "";
    local maxLevel = FormulaM.invoke("GET_ARTIFACT_MAX_LEVEL");

    local department = query(classId, "department");

    for lv = 1, level do

        local rank = EquipM.query(classId, "rank");
        local props = FormulaM.invoke("GET_ARTIFACT_LEVEL_PROP", classId, lv);
        local temp = string.format(getLocStr("artifact_level_up"), lv);
        local temp1 = "";
        local index = 0;
        for _, prop in pairs(props) do
            index = index + 1;
            local row = PropM.getRow(prop[1]);
            if row.rule == "career_strengthen" then
                temp1 = temp1 .. PropM.getSpecialDesc(prop, classId);
            else
                temp1 = temp1 .. extractString(PropM.getPropDesc(prop));
            end
            if #props > 1 and index < #props then
                temp1 = temp1 .. getLocStr("seperator");
            end

        end
        desc = desc.. temp .. temp1;
        desc = desc .. "\n";
    end

    if level <= maxLevel then
        desc = desc.."[gray]"
        for lv = level + 1, maxLevel do
            local rank = EquipM.query(classId, "rank");
            local props = FormulaM.invoke("GET_ARTIFACT_LEVEL_PROP", classId, lv);

            local temp = string.format(getLocStr("artifact_level_up"), lv);
            local temp1 = "";
            local index = 0;
            for _, prop in pairs(props) do
                index = index + 1;
                local row = PropM.getRow(prop[1]);
                if row.rule == "career_strengthen" then
                    temp1 = temp1 .. PropM.getSpecialDesc(prop, classId);
                else
                    temp1 = temp1 .. extractString(PropM.getPropDesc(prop));
                end

                if #props > 1 and index < #props then
                    temp1 = temp1 .. getLocStr("seperator");
                end
            end

            desc = desc..temp..temp1;

            if lv ~= maxLevel then
                desc = desc .. "\n";
            end
        end
        desc = desc .. "[-]"
    end

    return desc;
end

-- 获取神器带来的附加属性
function getExtraProps(classId)
    -- 神器等级
    local level = getArtifactLevel(classId);

    -- 神器星级
    local rank = EquipM.query(classId, "rank");
    local ret = {};

    for i = 1, level do
        local props = FormulaM.invoke("GET_ARTIFACT_LEVEL_PROP", classId, i);
        ret = table.append(ret, props);
    end

    return ret;
end

whenDoItemBonus = function(bonus)
    if bonus[1] ~= 1 then
        -- 不是物品，不处理
        return;
    end

    local classId = bonus[2];

    -- 初始化神器耐久
    if isArtifact(classId) then
        local maxDurability = query(classId, "max_durability");
        setDurability(classId, maxDurability);
    end
end

-- 获取3个系别的神器
function getDepartmentIds()
    local ret = {};
    ret = {["risker"] = riskerIds, ["wizard"] = wizardIds, ["fighter"] = fighterIds};
    return ret;
end

-- 根据神器系别返回神器
function getIdsByDepartment(department)

    if department == EQUIP_STYLE_SPECIAL then
        return riskerIds;
    elseif department == EQUIP_STYLE_PHYSIC then
        return fighterIds;
    else
        return wizardIds;
    end
end

-- 获取所有神器或神器碎片Id
function getAllArtifactIds(isScrap)
    local allIds = {};
    allIds = table.append(allIds, riskerIds);
    allIds = table.append(allIds, wizardIds);
    allIds = table.append(allIds, fighterIds);
    if isScrap then
        local scraps = {};
        for _, id in pairs(allIds) do
            table.insert(scraps, query(id, "scrap"));
        end
        return scraps;
    else
        return allIds;
    end
end

-- 根据当前的遗物，获得对应的系别
function getDepartmentByRelicId(classId)
    if classId == RISK_RELIC_ID then
        return EQUIP_STYLE_SPECIAL;
    elseif classId == FIGHT_RELIC_ID then
        return EQUIP_STYLE_PHYSIC;
    else
        return EQUIP_STYLE_MAGIC;
    end
end

-- 根据神器获得对应的遗物
function getRelicIdByArtifact(classId)
    local department = query(classId, "department");
    if department == EQUIP_STYLE_SPECIAL then
        return RISK_RELIC_ID;
    elseif department == EQUIP_STYLE_PHYSIC then
        return FIGHT_RELIC_ID;
    else
        return WIZARD_RELIC_ID;
    end
end

-- 判断能否购买遗物
function canBuyHeroRelic(classId, num)
    local sellPrice = ItemM.query(classId, "sell_price");
    local cost = sellPrice[1];

    if cost[1] == 1 then
        return ItemM.getAmount(ME.user, cost[2]) >= cost[3] * num;
    elseif cost[1] == 2 then
        return ME.user.dbase:query(cost[2], 0) >= cost[3] * num;
    end

    return false;
end

-- 购买遗物
function buyHeroRelic(classId, num)
    -- 能否购买
    if not canBuyHeroRelic(classId, num) then
        return false;
    end

    local sellPrice = ItemM.query(classId, "sell_price");
    local cost = sellPrice[1];

    if cost[1] == 1 then
        if not ItemM.costAmount(ME.user, cost[2], cost[3] * num) then
            return false;
        end
    elseif cost[1] == 2 then
        if not ME.user:costAttrib(cost[2], cost[3] * num) then
            return false;
        end
    end

    -- 奖励
    BonusM.doBonus({1, classId, num}, "buy_hero_relic");

    -- 抛出事件
    EventMgr.fire(event.BUY_HERO_RELIC, { ["class_id"] = class_id, ["amount"] = num });
    return true;
end

-- 使用遗物
function useHeroRelic(classId, num)
    -- 扣除
    if not ItemM.costAmount(ME.user, classId, num) then
        return false;
    end

    local bonusList = {};
    for i = 1, num do
        -- 获取一个随机数
        local seed = RandomFactoryM.myRandom(ME.user);
        -- 根据类型计算奖励
        bonus = FormulaM.invoke("CALC_HERO_RELIC_BONUS", classId, seed);
        -- 直接奖励
        BonusM.doBonus(bonus, "hero_relic");
        -- 收集奖励信息
        table.insert(bonusList, bonus);
    end

    -- 抛出事件
    EventMgr.fire(event.USE_HERO_RELIC, { ["bonusList"] = bonusList, ["classId"] = classId});
    return true;
end
